// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 执行跟踪程序。
// 跟踪器捕获范围广泛的执行事件，如goroutine 
// 创建/阻止/取消阻止、系统调用进入/退出/阻止、GC相关事件、
// 堆大小的更改、处理器启动/停止等，并以压缩形式将它们写入缓冲区
// 中。大多数事件都会捕获精确的纳秒级时间戳和堆栈
// 跟踪。
// 见https:

package runtime

import (
	"runtime/internal/atomic"
	"runtime/internal/sys"
	"unsafe"
)

// 事件类型在跟踪中，参数用方括号表示。
const (
	traceEvNone              = 0  // 未使用的
	traceEvBatch             = 1  // 每P批事件的开始[pid，时间戳]
	traceEvFrequency         = 2  // 包含跟踪计时器频率[频率（每秒节拍）]
	traceEvStack             = 3  // 堆栈[堆栈id，PC数量，{PC数组，函数字符串id，文件字符串id，行}]
	traceEvGomaxprocs        = 4  // GOMAXPROCS的当前值[timestamp，GOMAXPROCS，stack id]
	traceEvProcStart         = 5  // P的开始[timestamp，线程id]
	traceEvProcStop          = 6  // P的停止[timestamp]
	traceEvGCStart           = 7  // GC start[timestamp，seq，stack id]
	traceEvGCDone            = 8  // GC done[timestamp]
	traceEvGCSTWStart        = 9  // GC STW start[timestamp，kind]
	traceEvGCSTWDone         = 10 // GC STW done[timestamp]
	traceEvGCSweepStart      = 11 // GC扫描开始[时间戳，堆栈id]
	traceEvGCSweepDone       = 12 // GC扫描完成[时间戳，扫描，回收]
	traceEvGoCreate          = 13 // goroutine创建[时间戳，新goroutine id，新堆栈id，堆栈id]
	traceEvGoStart           = 14 // goroutine开始运行[时间戳，goroutine id，seq]
	traceEvGoEnd             = 15 // goroutine结束[时间戳]
	traceEvGoStop            = 16 // goroutine停止（如select{}）[时间戳，堆栈]
	traceEvGoSched           = 17 // goroutine调用Gosched[timestamp，堆栈]
	traceEvGoPreempt         = 18 // goroutine被抢占[timestamp，堆栈]
	traceEvGoSleep           = 19 // goroutine调用睡眠[timestamp，堆栈]
	traceEvGoBlock           = 20 // goroutine块[timestamp，堆栈]
	traceEvGoUnblock         = 21 // goroutine未被阻止[timestamp，goroutine id，seq，stack]
	traceEvGoBlockSend       = 22 // chan send上的goroutine块[timestamp，stack]
	traceEvGoBlockRecv       = 23 // chan recv上的goroutine块[timestamp，stack]
	traceEvGoBlockSelect     = 24 // select上的goroutine块[timestamp，stack]
	traceEvGoBlockSync       = 25 // Mutex Mutex上的goroutine块[timestamp，stack]
	traceEvGoBlockCond       = 26 // goroutine blocks on Cond[timestamp，stack]
	traceEvGoBlockNet        = 27 // goroutine blocks on network[timestamp，stack]
	traceEvGoSysCall         = 28 // syscall enter[timestamp，stack]
	traceEvGoSysExit         = 29 // syscall exit[timestamp，goroutine id，seq，real timestamp]
	traceEvGoWaiting         = 31 // 表示当跟踪开始[timestamp，goroutine id]
	traceEvGoInSyscall       = 32 // 表示goroutine在跟踪开始[timestamp，goroutine id]
	traceEvHeapAlloc         = 33 // gcController.heapLive change[timestamp，heap_alloc]
	traceEvHeapGoal          = 34 // gcController.heapGoal（以前的next_gc）更改时处于系统调用中[timestamp，heap goal in bytes]
	traceEvTimerGoroutine    = 35 // 当前未使用；以前表示的计时器goroutine[timer goroutine id]
	traceEvFutileWakeup      = 36 // 表示此goroutine的上次唤醒无效[timestamp]
	traceEvString            = 37 // 字符串字典条目[id，length，string]
	traceEvGoStartLocal      = 38 // goroutine在与最后一个事件[timestamp，goroutine id]
	traceEvGoUnblockLocal    = 39 // goroutine在与最后一个事件[timestamp，goroutine id，stack]相同的P上被解除阻止，goroutine在与最后一个事件[timestamp，goroutine id，real timestamp]相同的P上运行
	traceEvGoStartLabel      = 41 // goroutine开始运行时使用标签[timestamp，goroutine id，seq，label string id]
	traceEvGoBlockGC         = 42 // GC-assist上的goroutine块[timestamp，stack]
	traceEvGCMarkAssistStart = 43 // GC mark-assist start[timestamp，stack]
	traceEvGCMarkAssistDone  = 44 // GC mark assist done[timestamp]
	traceEvUserTaskCreate    = 45 // trace.NewContext[timestamp，internal task id，internal parent task id，stack，name string]
	traceEvUserTaskEnd       = 46 // 任务结束[timestamp，internal task id，stack]
	traceEvUserRegion        = 47 // trace.WithRegion[timestamp，internal task id，mode（0:start，1:end），stack，name string]
	traceEvUserLog           = 48 // trace.Log[时间戳、内部任务id、键字符串id、堆栈、值字符串]
	traceEvCount             = 49
	// 使用字节，但只有6位可用于事件类型。
	// 剩余的2位用于指定参数数量。
	// 这意味着最大事件类型值为63。
)

const (
	// 跟踪中的时间戳为cputicks/tractickdiv。
	// 这是绝对值时间戳的差异较小，因此它们的编码字节数较少。x86上的时间戳
	// 512 MHz根据功率ISA v2.07第6.2节，因此我们在ppc64 
	// 和ppc64le上使用16。
	// 在nanotime模拟cputicks的体系结构中，跟踪无法可靠工作，因此该值对这些体系结构并不重要。
	traceTickDiv = 16 + 48*(sys.Goarch386|sys.GoarchAmd64)
	// 单堆栈跟踪中的最大PC数。
	// 由于事件只包含堆栈id而不包含整个堆栈跟踪，
	// 我们可以在这里允许相当大的值。
	traceStackSize = 128
	// 在跟踪时不使用真实P时使用的假P的标识符。
	traceGlobProc = -1
	// 在base-128中编码uint64的最大字节数。
	traceBytesPerNumber = 10
	// 第一个事件字节中参数数量的移位。
	traceArgCountShift = 6
	// 标志传递给traceGoPark，表示此
	// goroutine的上一次唤醒无效。例如，一个goroutine在一个互斥体上被解锁，
	// 但是另一个goroutine在第一个
	// goroutine被调度之前抢先获得了互斥体，因此第一个goroutine必须再次阻塞。
	// 此类唤醒发生在缓冲通道和sync.Mutex上，
	// 但最终用户通常对此不感兴趣。
	traceFutileWakeup byte = 128
)

// 跟踪是全局跟踪上下文。
var trace struct {
	lock          mutex       // 保护以下成员
	lockOwner     *g          // 以避免递归锁锁定期间的死锁
	enabled       bool        // 当设置运行时跟踪事件
	shutdown      bool        // 在设置为false后等待跟踪读取器完成时设置
	headerWritten bool        // ReadTrace是否已发出跟踪头
	footerWritten bool        // ReadTrace是否已发出跟踪页脚
	shutdownSema  uint32      // 用于等待ReadTrace完成
	seqStart      uint64      // 启动跟踪时的序列号
	ticksStart    int64       // 启动跟踪时的cputicks 
	ticksEnd      int64       // 停止跟踪时的cputicks 
	timeStart     int64       // 启动跟踪时的纳米时间已启动
	timeEnd       int64       // n跟踪停止时的时间
	seqGC         uint64      // GC启动/完成序列器
	reading       traceBufPtr // 当前移交给用户的缓冲区
	empty         traceBufPtr // 空缓冲区堆栈
	fullHead      traceBufPtr // 满缓冲区队列
	fullTail      traceBufPtr
	reader        guintptr        // 调用ReadTrace的goroutine，或nil 
	stackTab      traceStackTable // 将堆栈跟踪映射到traceEvString的唯一ID 

	// 字典。
	// 
	// TODO:访问地图的中控锁不理想。
	// 选项：为所有用户批注区域名称和标记预先分配ID 
	// 选项：per-P缓存
	// 选项：sync.Map-like数据结构
	stringsLock mutex
	strings     map[string]uint64
	stringSeq   uint64

	// markWorkerLabels将gcMarkWorkerMode映射到字符串ID。
	markWorkerLabels [len(gcMarkWorkerModeStrings)]uint64

	bufLock mutex       // 保护buf 
	buf     traceBufPtr // 全局跟踪缓冲区，在没有p 
}

// traceBufHeader的情况下运行时使用per-p跟踪缓冲区。
type traceBufHeader struct {
	link      traceBufPtr             // 在trace.empty/full 
	lastTicks uint64                  // 当我们编写上一个事件
	pos       int                     // arr 
}

// traceBuf是per-P跟踪缓冲区。
// 
// go:notinheap 
type traceBuf struct {
	traceBufHeader
	arr [64<<10 - unsafe.Sizeof(traceBufHeader{})]byte // traceBufHeader.buf 
}

// traceBufftr是一个不被垃圾
// 收集器跟踪的*traceBuf，并且没有写障碍。TraceBuf不是从GC的堆中分配的
// 因此这是安全的，并且通常在不允许写屏障的上下文中操作，因此
// 这是必要的。
// 
// TODO:因为traceBuf现在是go:notinheap，所以没有必要这样做。
type traceBufPtr uintptr

func (tp traceBufPtr) ptr() *traceBuf   { return (*traceBuf)(unsafe.Pointer(tp)) }
func (tp *traceBufPtr) set(b *traceBuf) { *tp = traceBufPtr(unsafe.Pointer(b)) }
func traceBufPtrOf(b *traceBuf) traceBufPtr {
	return traceBufPtr(unsafe.Pointer(b))
}

// StartTrace启用对当前进程的跟踪。
// 跟踪时，数据将被缓冲，并通过ReadTrace可用。
// StartTrace如果已启用跟踪，则返回错误。
// 大多数客户端应该使用运行时/跟踪包或测试包的
// /-test.trace标志，而不是直接调用StartTrace。
func StartTrace() error {
	// 停止这个世界，这样我们就可以在跟踪开始时对所有goroutine进行一致的快照。
	// 不要在GC期间停止世界，因此我们确保始终看到
	// 对GC相关事件的一致看法（例如，开始总是
	// 与结束配对）。
	stopTheWorldGC("start tracing")

	// 防止sysmon运行任何可能生成事件的代码。
	lock(&sched.sysmonlock)

	// 我们正处于停止状态，但是系统调用可以同时完成并写入跟踪。
	// Exitsyscall可以检查trace.enabled很久以前，然后突然唤醒
	// 并决定在随机时间点写入跟踪。
	// 但是，这样的系统调用将使用global trace.buf缓冲区，因为我们使用了
	// 通过停止世界而获得了所有的p。所以这保护我们不受这些种族的影响。
	lock(&trace.bufLock)

	if trace.enabled || trace.shutdown {
		unlock(&trace.bufLock)
		unlock(&sched.sysmonlock)
		startTheWorldGC()
		return errorString("tracing is already enabled")
	}

	// 尚无法设置trace.enabled。当世界停止时，如果我们在此处设置trace.enabled，exitsyscall可能会发出延迟事件（请参阅exitsyscall中的exitTicks）。
	// 这将导致不一致的跟踪：
	// -要么GOSSEXIT出现在EVGOINSYSCAL之前，要么
	// -要么GOSSEXIT出现在我们下面没有发出EvGoInSyscall的goroutine中。
	// 为了指示traceEvent不能忽略下面的事件，我们设置了startingtrace。
	// trace.enabled在我们发出所有初步事件之后设置。
	_g_ := getg()
	_g_.m.startingtrace = true

	// 获取当前堆栈ID，以便在下面的所有traceEvGoCreate事件中使用。
	mp := acquirem()
	stkBuf := make([]uintptr, traceStackSize)
	stackID := traceStackID(mp, stkBuf, 2)
	releasem(mp)

	// 世界已停止，无需锁定。
	forEachGRace(func(gp *g) {
		status := readgstatus(gp)
		if status != _Gdead {
			gp.traceseq = 0
			gp.tracelastp = getg().m.p
			// /+PCQuantum，因为traceFrameForPC期望返回PCs并减去PCQuantum。
			id := trace.stackTab.put([]uintptr{gp.startpc + sys.PCQuantum})
			traceEvent(traceEvGoCreate, -1, uint64(gp.goid), uint64(id), stackID)
		}
		if status == _Gwaiting {
			// traceEvGoWaiting表示seq=1。
			gp.traceseq++
			traceEvent(traceEvGoWaiting, -1, uint64(gp.goid))
		}
		if status == _Gsyscall {
			gp.traceseq++
			traceEvent(traceEvGoInSyscall, -1, uint64(gp.goid))
		} else {
			gp.sysblocktraced = false
		}
	})
	traceProcStart()
	traceGoStart()
	// 注意：需要在发出traceEvGoInSyscall事件后设置ticksStart。
	// 如果我们用另一种方式执行，则exitsyscall可能会在ticksStart之后但在traceEvGoInSyscall时间戳之前查询sysexitticks。
	// 这将导致错误的结论，即cputicks已损坏。
	trace.ticksStart = cputicks()
	trace.timeStart = nanotime()
	trace.headerWritten = false
	trace.footerWritten = false

	// 字符串到id映射
	// 0:为空字符串保留
	// 剩余：由traceString注册的其他字符串
	trace.stringSeq = 0
	trace.strings = make(map[string]uint64)

	trace.seqGC = 0
	_g_.m.startingtrace = false
	trace.enabled = true

	// 注册运行时goroutine标签。
	_, pid, bufp := traceAcquireBuffer()
	for i, label := range gcMarkWorkerModeStrings[:] {
		trace.markWorkerLabels[i], bufp = traceString(bufp, pid, label)
	}
	traceReleaseBuffer(pid)

	unlock(&trace.bufLock)

	unlock(&sched.sysmonlock)

	startTheWorldGC()
	return nil
}

// 如果以前启用了StopTrace，则停止跟踪。
// StopTrace仅在跟踪的所有读取完成后返回。
func StopTrace() {
	// 停止这个世界，这样我们就可以从下面的所有p中收集跟踪缓冲区，
	// 并且避免与traceEvent的竞争。
	stopTheWorldGC("stop tracing")

	// 请参见StartTrace中的注释。
	lock(&sched.sysmonlock)

	// 请参见StartTrace中的注释。
	lock(&trace.bufLock)

	if !trace.enabled {
		unlock(&trace.bufLock)
		unlock(&sched.sysmonlock)
		startTheWorldGC()
		return
	}

	traceGoSched()

	// 在所有分配的Ps上循环，因为死Ps可能仍然有
	// 跟踪缓冲区。
	for _, p := range allp[:cap(allp)] {
		buf := p.tracebuf
		if buf != 0 {
			traceFullQueue(buf)
			p.tracebuf = 0
		}
	}
	if trace.buf != 0 {
		buf := trace.buf
		trace.buf = 0
		if buf.ptr().pos != 0 {
			traceFullQueue(buf)
		}
	}

	for {
		trace.ticksEnd = cputicks()
		trace.timeEnd = nanotime()
		// Windows时间只能每15毫秒滴答一次，请至少等待一次滴答声。
		if trace.timeEnd != trace.timeStart {
			break
		}
		osyield()
	}

	trace.enabled = false
	trace.shutdown = true
	unlock(&trace.bufLock)

	unlock(&sched.sysmonlock)

	startTheWorldGC()

	// 世界已启动，但我们已设置trace.shutdown，因此无法启动新的跟踪。
	// 等待跟踪读取器刷新挂起的缓冲区并停止。
	semacquire(&trace.shutdownSema)
	if raceenabled {
		raceacquire(unsafe.Pointer(&trace.shutdownSema))
	}

	// 锁保护我们不受StartTrace/StopTrace比赛的影响，因为它们确实阻止了世界。
	lock(&trace.lock)
	for _, p := range allp[:cap(allp)] {
		if p.tracebuf != 0 {
			throw("trace: non-empty trace buffer in proc")
		}
	}
	if trace.buf != 0 {
		throw("trace: non-empty global trace buffer")
	}
	if trace.fullHead != 0 || trace.fullTail != 0 {
		throw("trace: non-empty full trace buffer")
	}
	if trace.reading != 0 || trace.reader != 0 {
		throw("trace: reading after shutdown")
	}
	for trace.empty != 0 {
		buf := trace.empty
		trace.empty = buf.ptr().link
		sysFree(unsafe.Pointer(buf), unsafe.Sizeof(*buf.ptr()), &memstats.other_sys)
	}
	trace.strings = nil
	trace.shutdown = false
	unlock(&trace.lock)
}

// ReadTrace返回下一个二进制跟踪数据块，直到数据
// 可用为止。如果关闭跟踪，并且返回了
// 打开时积累的所有数据，则ReadTrace返回nil。在再次调用ReadTrace之前，调用方必须复制
// 返回的数据。
// 每次必须从一个goroutine调用ReadTrace。
func ReadTrace() []byte {
	// 此函数可能需要锁定trace.lock递归
	// （goparkulock->traceGoPark->traceEvent->traceFlush）。
	// 为了允许这样做，我们使用trace.lockOwner。
	// 同时此函数在保持trace.lock时不能分配：
	// 分配可以调用heap allocate，它将在保持heap lock时尝试发出跟踪
	// 事件。
	lock(&trace.lock)
	trace.lockOwner = getg()

	if trace.reader != 0 {
		// 多个goroutine读取跟踪。这很糟糕。
		// 但我们不希望因为跟踪而导致程序崩溃，因为可以在prod服务器上运行时启用跟踪。
		trace.lockOwner = nil
		unlock(&trace.lock)
		println("runtime: ReadTrace called from multiple goroutines simultaneously")
		return nil
	}
	// 回收旧的缓冲区。
	if buf := trace.reading; buf != 0 {
		buf.ptr().link = trace.empty
		trace.empty = buf
		trace.reading = 0
	}
	// 写入跟踪头。
	if !trace.headerWritten {
		trace.headerWritten = true
		trace.lockOwner = nil
		unlock(&trace.lock)
		return []byte("go 1.11 trace\x00\x00\x00")
	}
	// 等待新数据。
	if trace.fullHead == 0 && !trace.shutdown {
		trace.reader.set(getg())
		goparkunlock(&trace.lock, waitReasonTraceReaderBlocked, traceEvGoBlock, 2)
		lock(&trace.lock)
	}
	// 写一个缓冲区。
	if trace.fullHead != 0 {
		buf := traceFullDequeue()
		trace.reading = buf
		trace.lockOwner = nil
		unlock(&trace.lock)
		return buf.ptr().arr[:buf.ptr().pos]
	}
	// 使用计时器频率写入页脚。
	if !trace.footerWritten {
		trace.footerWritten = true
		// 使用float64，因为（trace.ticksEnd-trace.ticksStart）*1e9可能会溢出int64。
		freq := float64(trace.ticksEnd-trace.ticksStart) * 1e9 / float64(trace.timeEnd-trace.timeStart) / traceTickDiv
		trace.lockOwner = nil
		unlock(&trace.lock)
		var data []byte
		data = append(data, traceEvFrequency|0<<traceArgCountShift)
		data = traceAppend(data, uint64(freq))
		// 这将发出一堆完整的缓冲区，我们将在下一次迭代中提取它们。
		trace.stackTab.dump()
		return data
	}
	// 完成。
	if trace.shutdown {
		trace.lockOwner = nil
		unlock(&trace.lock)
		if raceenabled {
			// trace.shutdownSema上的模型同步，它的竞争
			// 检测器看不到。这是为了避免在传递给trace.Start的写入程序上出现错误的
			// 竞赛报告。
			racerelease(unsafe.Pointer(&trace.shutdownSema))
		}
		// trace.enabled已重置，因此可以调用可跟踪函数。
		semrelease(&trace.shutdownSema)
		return nil
	}
	// 也很糟糕，但请参见上面的评论。
	trace.lockOwner = nil
	unlock(&trace.lock)
	println("runtime: spurious wakeup of trace reader")
	return nil
}

// traceReader返回应该唤醒的跟踪读取器（如果有）。
func traceReader() *g {
	if trace.reader == 0 || (trace.fullHead == 0 && !trace.shutdown) {
		return nil
	}
	lock(&trace.lock)
	if trace.reader == 0 || (trace.fullHead == 0 && !trace.shutdown) {
		unlock(&trace.lock)
		return nil
	}
	gp := trace.reader.ptr()
	trace.reader.set(nil)
	unlock(&trace.lock)
	return gp
}

// traceProcFree释放与pp关联的跟踪缓冲区。
func traceProcFree(pp *p) {
	buf := pp.tracebuf
	pp.tracebuf = 0
	if buf == 0 {
		return
	}
	lock(&trace.lock)
	traceFullQueue(buf)
	unlock(&trace.lock)
}

// TraceFillQueue队列buf进入满缓冲区队列。
func traceFullQueue(buf traceBufPtr) {
	buf.ptr().link = 0
	if trace.fullHead == 0 {
		trace.fullHead = buf
	} else {
		trace.fullTail.ptr().link = buf
	}
	trace.fullTail = buf
}

// TraceFiledQueue从满缓冲区队列中退出。
func traceFullDequeue() traceBufPtr {
	buf := trace.fullHead
	if buf == 0 {
		return 0
	}
	trace.fullHead = buf.ptr().link
	if trace.fullHead == 0 {
		trace.fullTail = 0
	}
	buf.ptr().link = 0
	return buf
}

// TraceeEvent将单个事件写入跟踪缓冲区，必要时刷新缓冲区。
// ev为事件类型。
// 如果跳过>0，则将当前堆栈id作为最后一个参数写入（跳过顶部帧）。
// 如果skip=0，则此事件类型应包含堆栈，但我们不希望
// 为此特定调用收集并记住它。
func traceEvent(ev byte, skip int, args ...uint64) {
	mp, pid, bufp := traceAcquireBuffer()
	// 既然我们已经完成了m.locks++并获得了bufLock，那么就启用了Double check trace.enabled。
	// 这可以防止traceEvent和StartTrace/StopTrace之间的争用。

	// 调用方检查trace.enabled==true，但trace.enabled可能在检查到现在之间已关闭。再查一遍。traceLockBuffer没有mp.locks++，
	// StopTrace没有停止World，StopWorld等待mp.locks返回零，
	// 因此，如果我们现在看到trace.enabled==true，我们知道函数的其余部分是真的。
	// Exitsyscall甚至可以在stopTheWorld期间运行。exitsyscall中跟踪期间与StartTrace/StopTrace 
	// 的竞争通过在traceLockBuffer中锁定trace.bufLock来解决。
	// 
	// 注意：trace_userTaskCreate运行相同的检查。
	if !trace.enabled && !mp.startingtrace {
		traceReleaseBuffer(pid)
		return
	}

	if skip > 0 {
		if getg() == mp.curg {
			skip++ // /+1，因为堆栈在traceEventLocked中捕获。
		}
	}
	traceEventLocked(0, mp, pid, bufp, ev, skip, args...)
	traceReleaseBuffer(pid)
}

func traceEventLocked(extraBytes int, mp *m, pid int32, bufp *traceBufPtr, ev byte, skip int, args ...uint64) {
	buf := bufp.ptr()
	// TODO:对非零外部字节参数进行测试。
	maxSize := 2 + 5*traceBytesPerNumber + extraBytes // 事件类型、长度、序列、时间戳、堆栈id和两个add参数
	if buf == nil || len(buf.arr)-buf.pos < maxSize {
		buf = traceFlush(traceBufPtrOf(buf), pid).ptr()
		bufp.set(buf)
	}

	ticks := uint64(cputicks()) / traceTickDiv
	tickDiff := ticks - buf.lastTicks
	buf.lastTicks = ticks
	narg := byte(len(args))
	if skip >= 0 {
		narg++
	}
	// 参数数量只有2位。
	// 如果数字>=3，则事件类型后面跟着以字节为单位的事件长度。
	if narg > 3 {
		narg = 3
	}
	startPos := buf.pos
	buf.byte(ev | narg<<traceArgCountShift)
	var lenp *byte
	if narg == 3 {
		// 为长度保留字节，假设长度<128。
		buf.varint(0)
		lenp = &buf.arr[buf.pos-1]
	}
	buf.varint(tickDiff)
	for _, a := range args {
		buf.varint(a)
	}
	if skip == 0 {
		buf.varint(0)
	} else if skip > 0 {
		buf.varint(traceStackID(mp, buf.stk[:], skip))
	}
	evSize := buf.pos - startPos
	if evSize > maxSize {
		throw("invalid length of trace event")
	}
	if lenp != nil {
		// 填写实际长度。
		*lenp = byte(evSize - 2)
	}
}

func traceStackID(mp *m, buf []uintptr, skip int) uint64 {
	_g_ := getg()
	gp := mp.curg
	var nstk int
	if gp == _g_ {
		nstk = callers(skip+1, buf)
	} else if gp != nil {
		gp = mp.curg
		nstk = gcallers(gp, skip, buf)
	}
	if nstk > 0 {
		nstk-- // skip runtime.goexit 
	}
	if nstk > 0 && gp.goid == 1 {
		nstk-- // skip runtime.main 
	}
	id := trace.stackTab.put(buf[:nstk])
	return uint64(id)
}

// TraceAquireBuffer返回要使用的跟踪缓冲区，如有必要，将其锁定。
func traceAcquireBuffer() (mp *m, pid int32, bufp *traceBufPtr) {
	mp = acquirem()
	if p := mp.p.ptr(); p != nil {
		return mp, p.id, &p.tracebuf
	}
	lock(&trace.bufLock)
	return mp, traceGlobProc, &trace.buf
}

// traceReleaseBuffer释放以前使用TraceAquireBuffer获取的缓冲区。
func traceReleaseBuffer(pid int32) {
	if pid == traceGlobProc {
		unlock(&trace.bufLock)
	}
	releasem(getg().m)
}

// traceFlush将buf放入满缓冲区堆栈，并返回空缓冲区。
func traceFlush(buf traceBufPtr, pid int32) traceBufPtr {
	owner := trace.lockOwner
	dolock := owner == nil || owner != getg().m.curg
	if dolock {
		lock(&trace.lock)
	}
	if buf != 0 {
		traceFullQueue(buf)
	}
	if trace.empty != 0 {
		buf = trace.empty
		trace.empty = buf.ptr().link
	} else {
		buf = traceBufPtr(sysAlloc(unsafe.Sizeof(traceBuf{}), &memstats.other_sys))
		if buf == 0 {
			throw("trace: out of memory")
		}
	}
	bufp := buf.ptr()
	bufp.link.set(nil)
	bufp.pos = 0

	// 初始化新批处理的缓冲区
	ticks := uint64(cputicks()) / traceTickDiv
	bufp.lastTicks = ticks
	bufp.byte(traceEvBatch | 1<<traceArgCountShift)
	bufp.varint(uint64(pid))
	bufp.varint(ticks)

	if dolock {
		unlock(&trace.lock)
	}
	return buf
}

// traceString将一个字符串添加到trace.strings并返回id。
func traceString(bufp *traceBufPtr, pid int32, s string) (uint64, *traceBufPtr) {
	if s == "" {
		return 0, bufp
	}

	lock(&trace.stringsLock)
	if raceenabled {
		// raceacquire是必需的，因为下面的映射访问
		// 带有种族注释。
		raceacquire(unsafe.Pointer(&trace.stringsLock))
	}

	if id, ok := trace.strings[s]; ok {
		if raceenabled {
			racerelease(unsafe.Pointer(&trace.stringsLock))
		}
		unlock(&trace.stringsLock)

		return id, bufp
	}

	trace.stringSeq++
	id := trace.stringSeq
	trace.strings[s] = id

	if raceenabled {
		racerelease(unsafe.Pointer(&trace.stringsLock))
	}
	unlock(&trace.stringsLock)

	// 上面的内存分配可能会触发跟踪，
	// 导致*bufp更改。下面的代码现在适用于*bufp，
	// 所以在这一点之后，必须没有导致跟踪的内存分配或任何活动。

	buf := bufp.ptr()
	size := 1 + 2*traceBytesPerNumber + len(s)
	if buf == nil || len(buf.arr)-buf.pos < size {
		buf = traceFlush(traceBufPtrOf(buf), pid).ptr()
		bufp.set(buf)
	}
	buf.byte(traceEvString)
	buf.varint(id)

	// 仔细检查字符串和长度是否合适。
	// 否则，截断字符串。
	slen := len(s)
	if room := len(buf.arr) - buf.pos; room < slen+traceBytesPerNumber {
		slen = room
	}

	buf.varint(uint64(slen))
	buf.pos += copy(buf.arr[buf.pos:], s[:slen])

	bufp.set(buf)
	return id, bufp
}

// traceAppend以little-endian-base-128编码方式将v附加到buf。
func traceAppend(buf []byte, v uint64) []byte {
	for ; v >= 0x80; v >>= 7 {
		buf = append(buf, 0x80|byte(v))
	}
	buf = append(buf, byte(v))
	return buf
}

// varint以little-endian-base-128编码将v附加到buf。
func (buf *traceBuf) varint(v uint64) {
	pos := buf.pos
	for ; v >= 0x80; v >>= 7 {
		buf.arr[pos] = 0x80 | byte(v)
		pos++
	}
	buf.arr[pos] = byte(v)
	pos++
	buf.pos = pos
}

// 字节在buf后面追加v。
func (buf *traceBuf) byte(v byte) {
	buf.arr[buf.pos] = v
	buf.pos++
}

// traceStackTable将堆栈跟踪（PC阵列）映射到唯一的uint32 ID。
// 读取时无锁。
type traceStackTable struct {
	lock mutex
	seq  uint32
	mem  traceAlloc
	tab  [1 << 13]traceStackPtr
}

// traceStack是traceStackTable中的单个堆栈。
type traceStack struct {
	link traceStackPtr
	hash uintptr
	id   uint32
	n    int
	stk  [0]uintptr // real type[n]uintptpr 
}

type traceStackPtr uintptr

func (tp traceStackPtr) ptr() *traceStack { return (*traceStack)(unsafe.Pointer(tp)) }

// 堆栈返回PC的切片。
func (ts *traceStack) stack() []uintptr {
	return (*[traceStackSize]uintptr)(unsafe.Pointer(&ts.stk))[:ts.n]
}

// put返回堆栈跟踪PC的唯一id并将其缓存在表中，
// 如果它第一次看到跟踪。
func (tab *traceStackTable) put(pcs []uintptr) uint32 {
	if len(pcs) == 0 {
		return 0
	}
	hash := memhash(unsafe.Pointer(&pcs[0]), 0, uintptr(len(pcs))*unsafe.Sizeof(pcs[0]))
	// 首先，搜索不带互斥的哈希表。
	if id := tab.find(pcs, hash); id != 0 {
		return id
	}
	// 现在，在互斥锁下进行双重检查。
	lock(&tab.lock)
	if id := tab.find(pcs, hash); id != 0 {
		unlock(&tab.lock)
		return id
	}
	// 创建新记录。
	tab.seq++
	stk := tab.newStack(len(pcs))
	stk.hash = hash
	stk.id = tab.seq
	stk.n = len(pcs)
	stkpc := stk.stack()
	for i, pc := range pcs {
		stkpc[i] = pc
	}
	part := int(hash % uintptr(len(tab.tab)))
	stk.link = tab.tab[part]
	atomicstorep(unsafe.Pointer(&tab.tab[part]), unsafe.Pointer(stk))
	unlock(&tab.lock)
	return stk.id
}

// 查找检查堆栈跟踪PC是否已存在于表中。
func (tab *traceStackTable) find(pcs []uintptr, hash uintptr) uint32 {
	part := int(hash % uintptr(len(tab.tab)))
Search:
	for stk := tab.tab[part].ptr(); stk != nil; stk = stk.link.ptr() {
		if stk.hash == hash && stk.n == len(pcs) {
			for i, stkpc := range stk.stack() {
				if stkpc != pcs[i] {
					continue Search
				}
			}
			return stk.id
		}
	}
	return 0
}

// newStack分配一个大小为n的新堆栈。
func (tab *traceStackTable) newStack(n int) *traceStack {
	return (*traceStack)(tab.mem.alloc(unsafe.Sizeof(traceStack{}) + uintptr(n)*sys.PtrSize))
}

// 所有帧返回与PC对应的所有帧。
func allFrames(pcs []uintptr) []Frame {
	frames := make([]Frame, 0, len(pcs))
	ci := CallersFrames(pcs)
	for {
		f, more := ci.Next()
		frames = append(frames, f)
		if !more {
			return frames
		}
	}
}

// 转储将所有以前缓存的堆栈写入跟踪缓冲区，
// 释放所有内存并重置状态。
func (tab *traceStackTable) dump() {
	var tmp [(2 + 4*traceStackSize) * traceBytesPerNumber]byte
	bufp := traceFlush(0, 0)
	for _, stk := range tab.tab {
		stk := stk.ptr()
		for ; stk != nil; stk = stk.link.ptr() {
			tmpbuf := tmp[:0]
			tmpbuf = traceAppend(tmpbuf, uint64(stk.id))
			frames := allFrames(stk.stack())
			tmpbuf = traceAppend(tmpbuf, uint64(len(frames)))
			for _, f := range frames {
				var frame traceFrame
				frame, bufp = traceFrameForPC(bufp, 0, f)
				tmpbuf = traceAppend(tmpbuf, uint64(f.PC))
				tmpbuf = traceAppend(tmpbuf, uint64(frame.funcID))
				tmpbuf = traceAppend(tmpbuf, uint64(frame.fileID))
				tmpbuf = traceAppend(tmpbuf, uint64(frame.line))
			}
			// 现在复制到缓冲区。
			size := 1 + traceBytesPerNumber + len(tmpbuf)
			if buf := bufp.ptr(); len(buf.arr)-buf.pos < size {
				bufp = traceFlush(bufp, 0)
			}
			buf := bufp.ptr()
			buf.byte(traceEvStack | 3<<traceArgCountShift)
			buf.varint(uint64(len(tmpbuf)))
			buf.pos += copy(buf.arr[buf.pos:], tmpbuf)
		}
	}

	lock(&trace.lock)
	traceFullQueue(bufp)
	unlock(&trace.lock)

	tab.mem.drop()
	*tab = traceStackTable{}
	lockInit(&((*tab).lock), lockRankTraceStackTab)
}

type traceFrame struct {
	funcID uint64
	fileID uint64
	line   uint64
}

// traceFrameForPC记录帧信息。
// 它可以分配内存。
func traceFrameForPC(buf traceBufPtr, pid int32, f Frame) (traceFrame, traceBufPtr) {
	bufp := &buf
	var frame traceFrame

	fn := f.Function
	const maxLen = 1 << 10
	if len(fn) > maxLen {
		fn = fn[len(fn)-maxLen:]
	}
	frame.funcID, bufp = traceString(bufp, pid, fn)
	frame.line = uint64(f.Line)
	file := f.File
	if len(file) > maxLen {
		file = file[len(file)-maxLen:]
	}
	frame.fileID, bufp = traceString(bufp, pid, file)
	return frame, (*bufp)
}

// traceAlloc是一个非线程安全的区域分配器。
// 它拥有一个traceAllocBlock的链接列表。
type traceAlloc struct {
	head traceAllocBlockPtr
	off  uintptr
}

// traceAllocBlock是traceAlloc中的一个块。
// 
// traceAllocBlock是从非GC的内存分配的，因此它不能包含堆指针。写入指向traceAllocBlocks的指针不需要写屏障。
// 
// go:notinheap 
type traceAllocBlock struct {
	next traceAllocBlockPtr
	data [64<<10 - sys.PtrSize]byte
}

// TODO:由于traceAllocBlock现在是go:notinheap，因此不需要这样做。
type traceAllocBlockPtr uintptr

func (p traceAllocBlockPtr) ptr() *traceAllocBlock   { return (*traceAllocBlock)(unsafe.Pointer(p)) }
func (p *traceAllocBlockPtr) set(x *traceAllocBlock) { *p = traceAllocBlockPtr(unsafe.Pointer(x)) }

// alloc分配n字节块。
func (a *traceAlloc) alloc(n uintptr) unsafe.Pointer {
	n = alignUp(n, sys.PtrSize)
	if a.head == 0 || a.off+n > uintptr(len(a.head.ptr().data)) {
		if n > uintptr(len(a.head.ptr().data)) {
			throw("trace: alloc too large")
		}
		block := (*traceAllocBlock)(sysAlloc(unsafe.Sizeof(traceAllocBlock{}), &memstats.other_sys))
		if block == nil {
			throw("trace: out of memory")
		}
		block.next.set(a.head.ptr())
		a.head.set(block)
		a.off = 0
	}
	p := &a.head.ptr().data[a.off]
	a.off += n
	return unsafe.Pointer(p)
}

// drop释放以前分配的所有内存并重置分配器。
func (a *traceAlloc) drop() {
	for a.head != 0 {
		block := a.head.ptr()
		a.head.set(block.next.ptr())
		sysFree(unsafe.Pointer(block), unsafe.Sizeof(traceAllocBlock{}), &memstats.other_sys)
	}
}

// 以下函数写入要跟踪的特定事件。

func traceGomaxprocs(procs int32) {
	traceEvent(traceEvGomaxprocs, 1, uint64(procs))
}

func traceProcStart() {
	traceEvent(traceEvProcStart, -1, uint64(getg().m.id))
}

func traceProcStop(pp *p) {
	// Sysmon和stopTheWorld可以停止系统调用中阻塞的Ps，
	// 为了处理此问题，我们临时使用了P.
	mp := acquirem()
	oldp := mp.p
	mp.p.set(pp)
	traceEvent(traceEvProcStop, -1)
	mp.p = oldp
	releasem(mp)
}

func traceGCStart() {
	traceEvent(traceEvGCStart, 3, trace.seqGC)
	trace.seqGC++
}

func traceGCDone() {
	traceEvent(traceEvGCDone, -1)
}

func traceGCSTWStart(kind int) {
	traceEvent(traceEvGCSTWStart, -1, uint64(kind))
}

func traceGCSTWDone() {
	traceEvent(traceEvGCSTWDone, -1)
}

// traceGCSweepStart准备跟踪扫描循环。在调用traceGCSweepSpan之前，这不会发出任何事件。
// 
// traceGCSweepStart必须与traceGCSweepDone配对，并且在这两个调用之间不能有抢占点。
func traceGCSweepStart() {
	// 延迟实际GCSweepStart事件，直到第一个跨度
	// 扫描。如果我们不扫描任何东西，就不会发出任何事件。
	_p_ := getg().m.p.ptr()
	if _p_.traceSweep {
		throw("double traceGCSweepStart")
	}
	_p_.traceSweep, _p_.traceSwept, _p_.traceReclaimed = true, 0, 0
}

// traceGCSweepSpan跟踪单个页面的扫描。
// 
// 这可以在traceGCSweepStart/tracegcsweepsdone 
// 对的外部调用；但是，在这种情况下，它不会发出任何跟踪事件。
func traceGCSweepSpan(bytesSwept uintptr) {
	_p_ := getg().m.p.ptr()
	if _p_.traceSweep {
		if _p_.traceSwept == 0 {
			traceEvent(traceEvGCSweepStart, 1)
		}
		_p_.traceSwept += bytesSwept
	}
}

func traceGCSweepDone() {
	_p_ := getg().m.p.ptr()
	if !_p_.traceSweep {
		throw("missing traceGCSweepStart")
	}
	if _p_.traceSwept != 0 {
		traceEvent(traceEvGCSweepDone, -1, uint64(_p_.traceSwept), uint64(_p_.traceReclaimed))
	}
	_p_.traceSweep = false
}

func traceGCMarkAssistStart() {
	traceEvent(traceEvGCMarkAssistStart, 1)
}

func traceGCMarkAssistDone() {
	traceEvent(traceEvGCMarkAssistDone, -1)
}

func traceGoCreate(newg *g, pc uintptr) {
	newg.traceseq = 0
	newg.tracelastp = getg().m.p
	// /+PCQuantum，因为traceFrameForPC期望返回PCs并减去PCQuantum。
	id := trace.stackTab.put([]uintptr{pc + sys.PCQuantum})
	traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(id))
}

func traceGoStart() {
	_g_ := getg().m.curg
	_p_ := _g_.m.p
	_g_.traceseq++
	if _p_.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker {
		traceEvent(traceEvGoStartLabel, -1, uint64(_g_.goid), _g_.traceseq, trace.markWorkerLabels[_p_.ptr().gcMarkWorkerMode])
	} else if _g_.tracelastp == _p_ {
		traceEvent(traceEvGoStartLocal, -1, uint64(_g_.goid))
	} else {
		_g_.tracelastp = _p_
		traceEvent(traceEvGoStart, -1, uint64(_g_.goid), _g_.traceseq)
	}
}

func traceGoEnd() {
	traceEvent(traceEvGoEnd, -1)
}

func traceGoSched() {
	_g_ := getg()
	_g_.tracelastp = _g_.m.p
	traceEvent(traceEvGoSched, 1)
}

func traceGoPreempt() {
	_g_ := getg()
	_g_.tracelastp = _g_.m.p
	traceEvent(traceEvGoPreempt, 1)
}

func traceGoPark(traceEv byte, skip int) {
	if traceEv&traceFutileWakeup != 0 {
		traceEvent(traceEvFutileWakeup, -1)
	}
	traceEvent(traceEv & ^traceFutileWakeup, skip)
}

func traceGoUnpark(gp *g, skip int) {
	_p_ := getg().m.p
	gp.traceseq++
	if gp.tracelastp == _p_ {
		traceEvent(traceEvGoUnblockLocal, skip, uint64(gp.goid))
	} else {
		gp.tracelastp = _p_
		traceEvent(traceEvGoUnblock, skip, uint64(gp.goid), gp.traceseq)
	}
}

func traceGoSysCall() {
	traceEvent(traceEvGoSysCall, 1)
}

func traceGoSysExit(ts int64) {
	if ts != 0 && ts < trace.ticksStart {
		// 初始化sysexitticks的代码
		// （在exitsyscall中，它运行时没有P，因此不是
		// 被世界其他地方停止）和初始化
		// 新跟踪的代码之间存在竞争。因此，必须将记录的SysExittick 
		// 视为“尽力而为”。如果它们对该跟踪有效，那么很好，
		// 使用它们以获得更高的准确性。但是，如果它们对于这个
		// 跟踪无效，那么假设跟踪是在实际的系统调用
		// 退出之后启动的（但在我们实际启动goroutine之前，
		// 现在），并分配一个新的时间戳以保持日志一致。
		ts = 0
	}
	_g_ := getg().m.curg
	_g_.traceseq++
	_g_.tracelastp = _g_.m.p
	traceEvent(traceEvGoSysExit, -1, uint64(_g_.goid), _g_.traceseq, uint64(ts)/traceTickDiv)
}

func traceGoSysBlock(pp *p) {
	// Sysmon和stopTheWorld可以将在远程Ps上运行的系统调用声明为已阻止，
	// 为了处理此问题，我们临时使用了P.
	mp := acquirem()
	oldp := mp.p
	mp.p.set(pp)
	traceEvent(traceEvGoSysBlock, -1)
	mp.p = oldp
	releasem(mp)
}

func traceHeapAlloc() {
	traceEvent(traceEvHeapAlloc, -1, gcController.heapLive)
}

func traceHeapGoal() {
	if heapGoal := atomic.Load64(&gcController.heapGoal); heapGoal == ^uint64(0) {
		// 禁用了基于堆的触发。
		traceEvent(traceEvHeapGoal, -1, 0)
	} else {
		traceEvent(traceEvHeapGoal, -1, heapGoal)
	}
}

// 从运行时/跟踪访问运行时函数。
// 参见runtime/trace/annotation.go 

// go:linkname trace\u userTaskCreate runtime/trace.userTaskCreate 
func trace_userTaskCreate(id, parentID uint64, taskType string) {
	if !trace.enabled {
		return
	}

	// 与traceEvent中的相同。
	mp, pid, bufp := traceAcquireBuffer()
	if !trace.enabled && !mp.startingtrace {
		traceReleaseBuffer(pid)
		return
	}

	typeStringID, bufp := traceString(bufp, pid, taskType)
	traceEventLocked(0, mp, pid, bufp, traceEvUserTaskCreate, 3, id, parentID, typeStringID)
	traceReleaseBuffer(pid)
}

// go:linkname trace\u userTaskEnd runtime/trace.userTaskEnd 
func trace_userTaskEnd(id uint64) {
	traceEvent(traceEvUserTaskEnd, 2, id)
}

// go:linkname trace\u userRegion runtime/trace.userRegion 
func trace_userRegion(id, mode uint64, name string) {
	if !trace.enabled {
		return
	}

	mp, pid, bufp := traceAcquireBuffer()
	if !trace.enabled && !mp.startingtrace {
		traceReleaseBuffer(pid)
		return
	}

	nameStringID, bufp := traceString(bufp, pid, name)
	traceEventLocked(0, mp, pid, bufp, traceEvUserRegion, 3, id, mode, nameStringID)
	traceReleaseBuffer(pid)
}

// go:linkname trace\u userLog runtime/trace.userLog 
func trace_userLog(id uint64, category, message string) {
	if !trace.enabled {
		return
	}

	mp, pid, bufp := traceAcquireBuffer()
	if !trace.enabled && !mp.startingtrace {
		traceReleaseBuffer(pid)
		return
	}

	categoryID, bufp := traceString(bufp, pid, category)

	extraSpace := traceBytesPerNumber + len(message) // 值字符串的额外空间
	traceEventLocked(extraSpace, mp, pid, bufp, traceEvUserLog, 3, id, categoryID)
	// traceEventLocked为val和len保留的额外空间（val）
	// 在buf中，因此buf现在有空间容纳以下内容。
	buf := bufp.ptr()

	// 仔细检查消息及其长度是否合适。
	// 否则，截断消息。
	slen := len(message)
	if room := len(buf.arr) - buf.pos; room < slen+traceBytesPerNumber {
		slen = room
	}
	buf.varint(uint64(slen))
	buf.pos += copy(buf.arr[buf.pos:], message[:slen])

	traceReleaseBuffer(pid)
}
